package com.world.rickcloudendpoint.api.handlers;

import com.world.rickcloudendpoint.common.constant.DetailErrorCodes;
import com.world.rickcloudendpoint.common.constant.ErrorCodes;
import com.world.rickcloudendpoint.common.exception.ClientException;
import com.world.rickcloudendpoint.common.exception.ErrorInfo;
import com.world.rickcloudendpoint.common.exception.ModuleException;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.transaction.TransactionException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;


import javax.servlet.http.HttpServletRequest;
//import javax.validation.ConstraintViolation;
//import javax.validation.ConstraintViolationException;
import java.util.List;

@RestControllerAdvice
@Log4j2
public class CommonExceptionHandler {

    private static final String DEFAULT_ERROR_VIEW = "error";

    @Value("${spring.application.name:UNKNOWN}")
    private String applicationName;

    @Value("${component.code:0}")
    private int componentCode;

    private ResponseEntity<ErrorInfo> error(HttpStatus httpRetCode, Integer errorCode, String message) {
        ErrorInfo error = new ErrorInfo();
        error.setCode(errorCode);
        error.setMessage(message);
        ResponseEntity<ErrorInfo> result = new ResponseEntity<>(error, httpRetCode);
        return result;
    }

    @ExceptionHandler(value = TransactionException.class)
    public ResponseEntity<ErrorInfo> handleTransactionException(
            HttpServletRequest request, TransactionException e) {
        log.error(applicationName + " module has a TransactionException", e);

        Integer errorCode = ErrorCodes
                .getApplicationServer(componentCode, DetailErrorCodes.Database_Opertate_Fail.value());
        String message = String.format("there is a %s exception in %s module,detail info is %s"
                , DetailErrorCodes.Database_Opertate_Fail.getReasonPhrase()
                , applicationName
                , e.getLocalizedMessage());
        return error(HttpStatus.INTERNAL_SERVER_ERROR, errorCode, message);
    }

    /**
     * 数据库异常直接抛出
     *
     * @param request 请求
     * @param e 异常
     * @return 异常包装类
     */
    @ExceptionHandler(value = DataAccessException.class)
    public ResponseEntity<ErrorInfo> handleSQLException(HttpServletRequest request,
            DataAccessException e) {
        log.error(applicationName + " module has a DataAccessException", e);
        Integer errorCode = ErrorCodes
                .getApplicationServer(componentCode, DetailErrorCodes.Database_Connect_Fail.value());
        String message = String.format("there is a %s exception in %s module,detail info is %s"
                , DetailErrorCodes.Database_Connect_Fail.getReasonPhrase(), applicationName, e.getLocalizedMessage());
        return error(HttpStatus.INTERNAL_SERVER_ERROR, errorCode, message);
    }

    /**
     * 参数转换异常处理
     *
     * @param request 请求
     * @param e 异常信息
     * @return 参数异常信息
     */
    @ExceptionHandler(value = MethodArgumentTypeMismatchException.class)
    public ResponseEntity<ErrorInfo> handleMethodArgumentTypeMismatch(HttpServletRequest request,
            MethodArgumentTypeMismatchException e) {
        log.error(applicationName + " module has a MethodArgumentTypeMismatchException", e);
        Integer errorCode = ErrorCodes.getApplicationUi(componentCode, DetailErrorCodes.Request_Param_Illegal.value());
        String message = String.format("there is a %s exception in %s module,detail info is %s"
                , DetailErrorCodes.Request_Param_Illegal.getReasonPhrase()
                , applicationName
                , e.getLocalizedMessage());
        return error(HttpStatus.BAD_REQUEST, errorCode, message);
    }

    @ExceptionHandler(value = Exception.class)
    public ResponseEntity<ErrorInfo> handleException(HttpServletRequest request, Exception e) {
        log.error(applicationName + " module has a Exception", e);
        Integer errorCode = ErrorCodes.getApplicationServer(componentCode, DetailErrorCodes.Unknown_Error.value());
        String message = String.format("there is a %s exception in %s module,detail info is %s"
                , DetailErrorCodes.Unknown_Error.getReasonPhrase()
                , applicationName
                , e.getLocalizedMessage());
        return error(HttpStatus.INTERNAL_SERVER_ERROR, errorCode, message);
    }

    @ExceptionHandler(value = ClientException.class)
    public ResponseEntity<ErrorInfo> handleClientException(HttpServletRequest request,
            ClientException e) {
        log.error(applicationName + " module has a ClientException");
        Integer errorCode = ErrorCodes.getApplicationUi(componentCode, e.getErrorCode());
        String message = String.format("there is a ClientException in %s module,detail info is %s"
                , applicationName, e.getErrorMsg());
        return error(HttpStatus.BAD_REQUEST, errorCode, message);
    }

    @ExceptionHandler(value = ModuleException.class)
    public ResponseEntity<ErrorInfo> handleModuleException(HttpServletRequest request,
            ModuleException e) {
        log.error(applicationName + " module has a ModuleException");
        Integer errorCode = ErrorCodes.getApplicationServer(componentCode, e.getErrorCode());
        String message = String.format("there is a ModuleException in %s module,detail info is %s"
                , applicationName, e.getErrorMsg());
        return error(HttpStatus.INTERNAL_SERVER_ERROR, errorCode, message);
    }

    @ExceptionHandler(value = MissingServletRequestParameterException.class)
    public ResponseEntity<ErrorInfo> handleMissingServletRequestParameterException(
            HttpServletRequest request, MissingServletRequestParameterException e) {
        log.error(applicationName + " module has a MissingServletRequestParameterException", e);
        Integer errorCode = ErrorCodes.getApplicationUi(componentCode, DetailErrorCodes.Request_Param_Null.value());
        String message = String.format("there is a %s exception in %s module,detail info is %s"
                , DetailErrorCodes.Request_Param_Null.getReasonPhrase(), applicationName, e.getLocalizedMessage());
        return error(HttpStatus.BAD_REQUEST, errorCode, message);
    }

    @ExceptionHandler(value = HttpMessageNotReadableException.class)
    public ResponseEntity<ErrorInfo> handleHttpMessageNotReadableException(
            HttpServletRequest request, HttpMessageNotReadableException e) {
        log.error(applicationName + " module has a HttpMessageNotReadableException", e);
        Integer errorCode = ErrorCodes.getApplicationUi(componentCode, DetailErrorCodes.Entity_Attribute_Illegal.value());
        String message = String.format("there is a %s exception in %s module,detail info is %s"
                , DetailErrorCodes.Entity_Attribute_Illegal.getReasonPhrase(), applicationName,
                e.getLocalizedMessage());
        return error(HttpStatus.BAD_REQUEST, errorCode, message);
    }

    @ExceptionHandler(value = BindException.class)
    public ResponseEntity<ErrorInfo> handleBindException(HttpServletRequest request,
            BindException e) {
        log.error(applicationName + " module has a BindException", e);
        BindingResult bindingResult = e.getBindingResult();
        Integer errorCode = ErrorCodes.getApplicationUi(componentCode, DetailErrorCodes.Entity_Attribute_Illegal.value());
        String message = String.format("there is a %s exception in %s module,detail info is "
                , DetailErrorCodes.Entity_Attribute_Illegal.getReasonPhrase(), applicationName);

        if (bindingResult.hasErrors()) {
            List<FieldError> errors = bindingResult.getFieldErrors();
            for (FieldError one : errors) {
                message += one.getObjectName() + "." + one.getField() + one.getDefaultMessage();
            }
        }
        return error(HttpStatus.BAD_REQUEST, errorCode, message);
    }

    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorInfo> handleMethodArgumentNotValidException(
            HttpServletRequest request, MethodArgumentNotValidException e) {
        log.error(applicationName + " module has a MethodArgumentNotValidException", e);
        BindingResult bindingResult = e.getBindingResult();
        Integer errorCode = ErrorCodes.getApplicationUi(componentCode, DetailErrorCodes.Request_Param_Illegal.value());
        String message = String.format("there is a %s exception in %s module,detail info is "
                , DetailErrorCodes.Request_Param_Illegal.getReasonPhrase()
                , applicationName);

        if (bindingResult.hasErrors()) {
            // 属性校验错误
            List<FieldError> errors = bindingResult.getFieldErrors();
            for (FieldError one : errors) {
                message += one.getObjectName() + "." + one.getField() + one.getDefaultMessage();
            }
            return error(HttpStatus.BAD_REQUEST, errorCode, message);
        }
        return error(HttpStatus.BAD_REQUEST, errorCode, message);
    }

    /**
     * 请求参数校验异常处理
     *
     * @param e 参数校验异常
     * @return 校验提示信息
     */
//    @ExceptionHandler(value = ConstraintViolationException.class)
//    public ResponseEntity<ErrorInfo> handleValidationException(ConstraintViolationException e) {
//        log.error(applicationName + " module has a ConstraintViolationException", e);
//        long errorCode = ErrorCodes.getApplicationUi(componentCode, DetailErrorCodes.Entity_Attribute_Illegal.value());
//        String message = String.format("there is a %s exception in %s module,detail info is "
//                , DetailErrorCodes.Entity_Attribute_Illegal.getReasonPhrase(), applicationName);
//        StringBuffer sbErrorMsg = new StringBuffer();
//        for (ConstraintViolation<?> s : e.getConstraintViolations()) {
//            // 去除接口中不含参数名称的校验信息
//            String propertyPath = String.valueOf(s.getPropertyPath());
//            String regEx = "^.*\\.arg\\d$";
//            Pattern pattern = Pattern.compile(regEx);
//            Matcher matcher = pattern.matcher(propertyPath);
//            if (matcher.find()) {
//                continue;
//            }
//
//            sbErrorMsg.append(propertyPath.substring(propertyPath.lastIndexOf('.') + 1)).append("：")
//                    .append(s.getMessage()).append("；");
//        }
//        message += sbErrorMsg.toString();
//        return error(HttpStatus.BAD_REQUEST, errorCode, message);
//    }

    @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
    public ResponseEntity<ErrorInfo> handleHttpRequestMethodNotSupportedException(
            HttpServletRequest request, HttpRequestMethodNotSupportedException e) {
        log.error(applicationName + " module has a HttpRequestMethodNotSupportedException", e);
        Integer errorCode = ErrorCodes.getApplicationUi(componentCode, DetailErrorCodes.Request_Param_Illegal.value());
        String message = String.format("there is a %s exception in %s module,detail info is %s"
                , DetailErrorCodes.Request_Param_Illegal.getReasonPhrase(), applicationName, e.getLocalizedMessage());
        return error(HttpStatus.BAD_REQUEST, errorCode, message);
    }

    @ExceptionHandler(value = HttpMediaTypeNotSupportedException.class)
    public ResponseEntity<ErrorInfo> handleHttpMediaTypeNotSupportedException(
            HttpServletRequest request, HttpMediaTypeNotSupportedException e) {
        log.error(applicationName + " module has a HttpMediaTypeNotSupportedException", e);
        Integer errorCode = ErrorCodes.getApplicationUi(componentCode, DetailErrorCodes.Request_Param_Illegal.value());
        String message = String.format("there is a %s exception in %s module,detail info is %s"
                , DetailErrorCodes.Request_Param_Illegal.getReasonPhrase()
                , applicationName
                , e.getLocalizedMessage());
        return error(HttpStatus.BAD_REQUEST, errorCode, message);
    }

    /**
     * 文件上传校验空文件路径异常
     *
     * @param request 请求
     * @param e MissingServletRequestPartException
     * @return
     */
    @ExceptionHandler(value = MissingServletRequestPartException.class)
    public ResponseEntity<ErrorInfo> handleMissingServletRequestPartException(HttpServletRequest request,
            MissingServletRequestPartException e) {
        log.error(applicationName + " module has a MissingServletRequestPartException", e);
        Integer errorCode = ErrorCodes.getApplicationUi(componentCode, DetailErrorCodes.Request_Param_Illegal.value());
        String message = String.format("there is a %s exception in %s module,detail info is %s"
                , DetailErrorCodes.Request_Param_Illegal.getReasonPhrase(), applicationName, e.getLocalizedMessage());
        return error(HttpStatus.BAD_REQUEST, errorCode, message);
    }

    /**
     * 非法参数异常处理
     * @param request
     * @param e
     * @return
     */
    @ExceptionHandler(value = IllegalArgumentException.class)
    public ResponseEntity<ErrorInfo> handleIllegalArgumentException(HttpServletRequest request,
                                                                    IllegalArgumentException e) {
        log.error(applicationName + " module has a IllegalArgumentException", e);
        Integer errorCode = ErrorCodes.getApplicationUi(componentCode, DetailErrorCodes.Request_Param_Illegal.value());
        String message = String.format("there is a %s exception in %s module,detail info is %s"
                , DetailErrorCodes.Request_Param_Illegal.getReasonPhrase()
                , applicationName
                , e.getLocalizedMessage());
        return error(HttpStatus.BAD_REQUEST, errorCode, message);
    }
}
